import * as fs from 'fs'
import { ctx } from '../utils/iface';
import { SessionOptions } from '../config';
import { join, dirname } from 'path';
import { getDataType, DataType } from '../utils/data';
import { mkdirs } from '../utils/file';
import { uuid } from '../utils/other';
export async function session(ctx: ctx, next) {
    if (ctx.control && ctx.control.m && ctx.control.c) {
        try {
            let sid: string = ctx.cookies.get(ctx.config.session.key);
            if (!sid) {
                sid = get_session_id(ctx)
                ctx.cookies.set(ctx.config.session.key, sid, {
                    maxAge: ctx.config.session.maxAge,
                    domain: ctx.config.session.domain,
                })
            }
            if (ctx.config.session.store) {
                let sess = ctx.config.session.store;
                ctx.session = new sess(sid, ctx.config.session)
            } else {
                ctx.session = new Session(sid, ctx.config.session)
            }
        } catch (error) {
            // console.error(error.message)
        } finally {
            await next()
            ctx.session.save()
        }
    } else {
        next()
    }
}
function get_session_id(ctx) {
    return uuid()
}
function encode(data: any) {
    let c = ""
    Object.keys(data).forEach((k) => {
        let b = Buffer.alloc(2)
        b[0] = k.length;
        b[1] = getDataType(data[k])
        c += `${b.toString('hex')}${k}`
        switch (b[1]) {
            case DataType.Boolean:
                c += data[k] ? '1' : '0'
                break;
            case DataType.String:
            case DataType.Number:
                c += data[k]
                break;
            case DataType.JSON:
                c += JSON.stringify(data[k])
                break;
            case DataType.Buffer:
                c += data[k].toString('hex')
                break;
        }
        c += "\n"
    })
    return c;
}
function decode(data: string): any {
    let c = data.split("\n")
    let o = {}
    c.forEach((d: string) => {
        let b = Buffer.from(d.substr(0, 4), 'hex')
        switch (b[1]) {
            case DataType.Boolean:
                o[d.substr(4, b[0])] = d.substr(4 + b[0], 1) == '1'
                break;
            case DataType.String:
                o[d.substr(4, b[0])] = d.substr(4 + b[0])
            case DataType.Number:
                o[d.substr(4, b[0])] = Number(d.substr(4 + b[0]))
                break;
            case DataType.JSON:
                o[d.substr(4, b[0])] = JSON.parse(d.substr(4 + b[0]))
                break;
            case DataType.Buffer:
                o[d.substr(4, b[0])] = Buffer.from(d.substr(4 + b[0]))
                break;
        }
    });
    return o;
}
export class Session {
    protected change: boolean = false;
    protected id: string = ''
    protected path: string = ''
    protected mtime: number = 0;
    protected data: { [index: string]: any } = {}
    constructor(id: string, options: SessionOptions) {
        this.path = join(options.path, id)
        this.id = id;
        try {
            this.data = decode(fs.readFileSync(this.path, { encoding: 'utf8' }))
        } catch (error) {
            // console.error(error.message)
        }
    }
    get(key: string) {
        return this.data[key]
    }
    set(key: string, value: any) {
        this.change = true;
        this.data[key] = value
    }
    save() {
        if (this.change)
            fs.writeFile(this.path, encode(this.data), (err) => {
                if (err) {
                    mkdirs(dirname(this.path))
                    this.save()
                }
            })
    }
    destroy() {
        return new Promise((s, j) => {
            fs.unlink(this.path, (err) => {
                if (err) { j(err) } else { s() }
            })
        })
    }
}